import { extend } from "../shared/index"

// 定义一个全局的依赖
let activeEffect
// 是否需要收集依赖
let shouldTrack

export class effectFn {
    private _fn:any
    deps = []
    active=true // 是否执行了stop方法
    onStop?:() => void
    constructor(fn,public scheduler?) {
        this._fn = fn
    }
    run() {
        if(!this.active) {
            return this._fn()
        }
        shouldTrack = true
        activeEffect = this
        const result = this._fn()
        // reset
        shouldTrack = false
        return result
    }
    stop() {
        if(this.active) {
            cleanupEffect(this)
            if(this.onStop) {
                this.onStop()
            }
            this.active = false
        }
    }
}

// 删除当前依赖
function cleanupEffect(effect) {
    effect.deps.forEach((dep:any) => {
        dep.delete(effect)
    })
    effect.deps.length = 0
}

const targetMap = new Map()

export function track(target,key) {
    if(!isTracking()) return
    let depsMap = targetMap.get(target)
    if(!depsMap) {
        depsMap = new Map()
        targetMap.set(target,depsMap)
    }
    let dep = depsMap.get(key)
    if(!dep) {
        dep = new Set()
        depsMap.set(key,dep)
    }
    trackEffects(dep)
}

export function trigger(target,key) {
    let depsMap = targetMap.get(target) 
    let dep = depsMap.get(key)
    triggerEffects(dep)
}

export function trackEffects(dep) {
    if(dep.has(activeEffect)) return
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
}
export function triggerEffects(dep) {
    for(let effect of dep) {
        if(effect.scheduler) {
            effect.scheduler()
        } else {
            effect.run()
        }
    }
}

export function isTracking() {
    return shouldTrack && activeEffect !== undefined
}

export function stop(runner) {
    runner.effect.stop()
}

export function effect(fn,options:any = {}) {
    const _effect = new effectFn(fn,options.scheduler)
    // options
    extend(_effect,options)
    _effect.run()
    const runner:any =  _effect.run.bind(_effect)
    runner.effect = _effect
    return runner
}
